package com.hj.widget;


import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.RectF;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewTreeObserver;

import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.core.view.MotionEventCompat;

import com.hj.R;

import java.util.ArrayList;
import java.util.List;

public class BitmapMatrixView extends View {
    private final Matrix baseMatrix = new Matrix();//View本身基层Matrix
    private final Matrix bitmapMatrix = new Matrix();//View本身Matrix
    private final Matrix tempMatrix = new Matrix();//临时Matrix
    private final Matrix saveMatrix = new Matrix();//临时保存改变前的Matrix
    private final Matrix canvasMatrix = new Matrix();//画布的Matrix
    private final Matrix restoreMatrix = new Matrix();//需要返回的Matrix
    private int mActionMode = -1;//当前的触摸模式
    private PointF downPoint = new PointF();//手指按下的位置
    private float downPointDistance;//两根手指按下的距离
    private PointF[] downPoints = new PointF[2];//两根手指按下的位置
    private PointF mMidPoint = new PointF();//两点间的中心点
    private final float[] matrixValues = new float[9];//四个坐标点和中心坐标
    private int width, height;//控件的大小
    private boolean drag = false;//是否支持拖拽
    private boolean zoom = false;//是否支持缩放
    private float borderLeft;//左侧边框宽度，默认0
    private float borderTop;//上部边框高度，默认0
    private float borderRight;//右侧边框宽度，默认0
    private float borderBottom;//底部边框高度，默认0
    private Paint borderPaint;//边框画笔
    private int borderAlpha;//边框的透明度
    private int borderColor;//边框颜色
    private float rectWidth;//内部区域宽度，默认控件内部宽度
    private float rectHeight;//内部区域高度，默认控件内部高度
    private int rectGravity;//内部区域位置，默认居中
    private int rectAlpha;//内部区域的透明度
    private boolean isRectSet;//内部区域的宽高是否已经固定
    private float rectCX, rectCY;//内部区域中心
    private Bitmap bitmap;//绘制的图片资源
    private Paint paint;//普通画笔
    private Paint bgPaint;//最外层-背景层画笔
    private Paint rectPaint;//内部层-内部区域层画笔
    private Paint bitmapPaint;//内部层Bitmap画笔
    private int alpha;//外部区域的透明度
    private long lastClickTime;//最近一次点击的时间
    private long clickTime;//点击的时间
    private Handler handler = new Handler(Looper.getMainLooper());
    private boolean isPointTouch = false;//手指是否按下
    private boolean showTouchLine;//手指按下是否显示表格线
    private int lineColor;//表格线的颜色
    private boolean autoSize;//拖动时是否自动回到边缘

    private List<LayerMatrix> matrixRecords = new ArrayList<>();//保存Maxtrix记录，可以用来还原


    public BitmapMatrixView(Context context) {
        super(context);
        init(context, null, 0, 0);
    }

    public BitmapMatrixView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs, 0, 0);
    }

    public BitmapMatrixView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs, defStyleAttr, 0);
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public BitmapMatrixView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init(context, attrs, defStyleAttr, defStyleRes);
    }

    private void init(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        if (attrs != null) {
            TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.BitmapMatrixView, defStyleAttr, defStyleRes);

            borderLeft = a.getDimension(R.styleable.BitmapMatrixView_borderLeft, 0);
            borderTop = a.getDimension(R.styleable.BitmapMatrixView_borderTop, 0);
            borderRight = a.getDimension(R.styleable.BitmapMatrixView_borderRight, 0);
            borderBottom = a.getDimension(R.styleable.BitmapMatrixView_borderBottom, 0);
            float border = a.getDimension(R.styleable.BitmapMatrixView_border, 0);
            if (border > 0) {
                borderLeft = borderTop = borderRight = borderBottom = border;
            }
            borderAlpha = (int) (255 * a.getFloat(R.styleable.BitmapMatrixView_borderAlpha, 1));
            borderColor = a.getColor(R.styleable.BitmapMatrixView_borderColor, Color.WHITE);

            rectGravity = a.getInt(R.styleable.BitmapMatrixView_rectGravity, Gravity.LEFT);
            rectAlpha = (int) (255 * a.getFloat(R.styleable.BitmapMatrixView_rectAlpha, 1));
            rectWidth = a.getDimension(R.styleable.BitmapMatrixView_rectWidth, 0);
            rectHeight = a.getDimension(R.styleable.BitmapMatrixView_rectHeight, 0);
            if (rectWidth > 0 && rectHeight > 0) {
                isRectSet = true;
            }

            alpha = (int) (255 * a.getFloat(R.styleable.BitmapMatrixView_alpha, 0));
            zoom = a.getBoolean(R.styleable.BitmapMatrixView_zoom, false);
            drag = a.getBoolean(R.styleable.BitmapMatrixView_drag, false);
            showTouchLine = a.getBoolean(R.styleable.BitmapMatrixView_showTouchLine, false);
            lineColor = a.getColor(R.styleable.BitmapMatrixView_lineColor, Color.WHITE);
            autoSize = a.getBoolean(R.styleable.BitmapMatrixView_autoSize, false);
            a.recycle();
        } else {
            borderLeft = 0;
            borderTop = 0;
            borderRight = 0;
            borderBottom = 0;
            borderAlpha = 0xFF;
            borderColor = Color.WHITE;
            rectGravity = Gravity.LEFT;
            rectAlpha = 0xFF;
            alpha = 0x00;
            isRectSet = false;
            drag = false;
            zoom = false;
            showTouchLine = false;
            lineColor = Color.WHITE;
            autoSize = false;
        }

        borderPaint = new Paint();
        borderPaint.setColor(borderColor);
        borderPaint.setAntiAlias(true);
        borderPaint.setStyle(Paint.Style.FILL);
        borderPaint.setAlpha(borderAlpha);

        paint = new Paint();
        paint.setAntiAlias(true);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(2);
        paint.setColor(Color.BLACK);

        bgPaint = new Paint();
        bgPaint.setAntiAlias(true);
        bgPaint.setAlpha(alpha);

        rectPaint = new Paint();
        rectPaint.setAntiAlias(true);
        rectPaint.setAlpha(rectAlpha);

        bitmapPaint = new Paint();
        bitmapPaint.setAntiAlias(true);

        getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                int preWidth = width;
                int preHeight = height;
                float preRectWidth = rectWidth;
                float preRectHeight = rectHeight;
                width = getWidth();
                height = getHeight();

                if (!isRectSet) {
                    //内部区域大小随控件改变而改变
                    rectWidth = getWidth() - getPaddingLeft() - getPaddingRight();
                    rectHeight = getHeight() - getPaddingTop() - getPaddingBottom();
                }
                if (preWidth != width || preHeight != height) {
                    if (viewSizeChangedListener != null) {
                        if (viewSizeChangedListener.onSizeChanged(preWidth, preHeight, width, height, preRectWidth, preRectHeight, rectWidth, rectHeight)) {
                            //外部主动更新，内部不处理
                            return;
                        }
                    }
                }

                postInvalidate();
            }
        });
    }

    public Bitmap getBitmap() {
        return bitmap;
    }

    public void setBitmap(Bitmap bitmap) {
        this.bitmap = bitmap;
        setLayerType(LAYER_TYPE_HARDWARE, null);
        postInvalidate();
    }

    public void setBitmap(Bitmap bitmap, int layerType) {
        this.bitmap = bitmap;
        setLayerType(layerType, null);
        postInvalidate();
    }

    public void setBitmap(Bitmap bitmap, Matrix matrix) {
        this.bitmap = bitmap;
        setBitmapMatrix(matrix);
    }

    public Bitmap getRectBitmap() {
        Bitmap retBitmap = Bitmap.createBitmap((int) rectWidth, (int) rectHeight, bitmap.getConfig());
        Canvas canvas = new Canvas(retBitmap);

        Matrix drawMatrix = new Matrix();
        drawMatrix.postConcat(baseMatrix);
        drawMatrix.postConcat(canvasMatrix);
        drawMatrix.postConcat(bitmapMatrix);

        PointF start = getStartPoint();
        drawMatrix.postTranslate(-start.x, -start.y);
        canvas.drawBitmap(bitmap, drawMatrix, bitmapPaint);

        Path rect = new Path();
        rect.addRect(new RectF(0, 0, rectWidth, rectHeight), Path.Direction.CW);
        Path border = new Path();
        border.moveTo(borderLeft, borderTop);
        border.lineTo(rectWidth - borderRight, borderTop);
        border.lineTo(rectWidth - borderRight, rectHeight - borderBottom);
        border.lineTo(borderLeft, rectHeight - borderBottom);
        border.close();
        rect.op(border, Path.Op.DIFFERENCE);

        paint.setStyle(Paint.Style.FILL);
        paint.setColor(Color.WHITE);
        canvas.drawPath(rect, paint);

        return retBitmap;
    }

    public boolean isAutoSize() {
        return autoSize;
    }

    public void setAutoSize(boolean autoSize) {
        this.autoSize = autoSize;
    }

    public Matrix getCanvasMatrix() {
        return canvasMatrix;
    }

    public void setCanvasMatrix(Matrix matrix) {
        this.canvasMatrix.set(matrix);
        postInvalidate();
    }

    public void setMatrix(Matrix matrix1, Matrix matrix2) {
        this.canvasMatrix.set(matrix1);
        this.baseMatrix.set(matrix2);
        postInvalidate();
    }

    public void setMatrix(Matrix matrix1, Matrix matrix2, Matrix matrix3) {
        this.canvasMatrix.set(matrix1);
        this.baseMatrix.set(matrix2);
        this.bitmapMatrix.set(matrix3);
        postInvalidate();
    }

    public void setBaseMatrix(Matrix matrix) {
        this.baseMatrix.set(matrix);
        postInvalidate();
    }

    public Matrix getBaseMatrix() {
        return baseMatrix;
    }

    /**
     * 判断是否已经保存过该图层
     *
     * @param name
     * @return
     */
    public int hasLayerMatrix(String name) {
        for (int i = 0; i < matrixRecords.size(); i++) {
            if (matrixRecords.get(i).equals(name)) {
                return i;
            }
        }
        return -1;
    }

    /**
     * 保存图层，已经存在则覆盖
     *
     * @param name
     * @return
     */
    public int saveLayer(String name) {
        int index = hasLayerMatrix(name);
        if (index > 0) {
            //已经存在，覆盖
            matrixRecords.set(index, new LayerMatrix(name, new Matrix(getBitmapMatrix())));
        } else {
            index = matrixRecords.size();
            matrixRecords.add(new LayerMatrix(name, new Matrix(getBitmapMatrix())));
        }
        return index;
    }

    /**
     * 获取图层
     *
     * @param layer
     * @return
     */
    public LayerMatrix getLayerMatrix(int layer) {
        if (layer >= 0 && layer < matrixRecords.size()) {
            return matrixRecords.get(layer);
        }
        return null;
    }

    /**
     * 清空缓存的图层
     */
    public void clearLayer() {
        matrixRecords.clear();
    }

    /**
     * 回退到该图层
     *
     * @param layer
     * @return
     */
    public void restoreToLayer(int layer) {
        if (layer >= 0 && layer < matrixRecords.size()) {
            for (int i = matrixRecords.size() - 1; i > layer; i--) {
                matrixRecords.remove(i);
            }
            setBitmapMatrix(matrixRecords.get(layer).matrix);
        }
    }

    /**
     * 回退到最初
     *
     * @return
     */
    public void restoreLayer() {
        if (matrixRecords.size() > 0) {
            setBitmapMatrix(matrixRecords.get(0).getMatrix());
            matrixRecords.clear();
        }
    }

    public int getLayerMatrixCount() {
        return matrixRecords.size();
    }

    public boolean isShowTouchLine() {
        return showTouchLine;
    }

    public void setShowTouchLine(boolean showTouchLine) {
        this.showTouchLine = showTouchLine;
    }

    /**
     * 改变绘制图像的Matrix
     *
     * @param m
     */
    public synchronized void setBitmapMatrix(Matrix m) {
        bitmapMatrix.set(m);
        postInvalidate();
    }

    /**
     * 获取当前图像绘制的Matrix
     *
     * @return
     */
    public Matrix getBitmapMatrix() {
        return bitmapMatrix;
    }

    public boolean isDrag() {
        return drag;
    }

    public void setDrag(boolean drag) {
        this.drag = drag;
    }

    public boolean isZoom() {
        return zoom;
    }

    public void setZoom(boolean zoom) {
        this.zoom = zoom;
    }

    /**
     * 边框的大小
     *
     * @param border dp为单位
     */
    public void setBorder(float border) {
        border = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, border, getContext().getResources().getDisplayMetrics());
        this.borderLeft = border;
        this.borderTop = border;
        this.borderRight = border;
        this.borderBottom = border;
        postInvalidate();
    }

    public void setBorderRect(float startX, float startY, float borderWidth, float borderHeight) {
        this.borderLeft = startX;
        this.borderTop = startY;
        this.borderRight = rectWidth - borderWidth - startX;
        this.borderBottom = rectHeight - borderHeight - startY;
        postInvalidate();
    }

    /**
     * 边框的大小
     *
     * @param borderLeft   dp为单位
     * @param borderTop    dp为单位
     * @param borderRight  dp为单位
     * @param borderBottom dp为单位
     */
    public void setBorder(float borderLeft, float borderTop, float borderRight, float borderBottom) {
        borderLeft = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, borderLeft, getContext().getResources().getDisplayMetrics());
        borderTop = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, borderTop, getContext().getResources().getDisplayMetrics());
        borderRight = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, borderRight, getContext().getResources().getDisplayMetrics());
        borderBottom = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, borderBottom, getContext().getResources().getDisplayMetrics());
        this.borderLeft = borderLeft;
        this.borderTop = borderTop;
        this.borderRight = borderRight;
        this.borderBottom = borderBottom;
        postInvalidate();
    }

    public float getBorderLeft() {
        return borderLeft;
    }

    public float getBorderTop() {
        return borderTop;
    }

    public float getBorderRight() {
        return borderRight;
    }

    public float getBorderBottom() {
        return borderBottom;
    }

    public int getBorderAlpha() {
        return borderAlpha;
    }

    public void setBorderAlpha(@IntRange(from = 0, to = 255) int borderAlpha) {
        this.borderAlpha = borderAlpha;
        borderPaint.setAlpha(borderAlpha);
        postInvalidate();
    }

    public int getBorderColor() {
        return borderColor;
    }

    public void setBorderColor(int borderColor) {
        this.borderColor = borderColor;
        borderPaint.setColor(borderColor);
        postInvalidate();
    }

    public float getMaxRectWidth() {
        return getWidth() - getPaddingLeft() - getPaddingRight();
    }

    public float getMaxRectHeight() {
        return getHeight() - getPaddingTop() - getPaddingBottom();
    }

    /**
     * 宽高比
     *
     * @param rate
     */
    public void setRect(float rate) {
        if (rate > 0) {
            isRectSet = true;
            float maxWidth = getWidth() - getPaddingLeft() - getPaddingRight();
            float maxHeight = getHeight() - getPaddingTop() - getPaddingBottom();
            if (rate > maxWidth / maxHeight) {
                this.rectWidth = maxWidth;
                this.rectHeight = maxWidth / rate;
            } else {
                this.rectWidth = rate * maxHeight;
                this.rectHeight = maxHeight;
            }
        } else {
            isRectSet = false;
            this.rectWidth = getWidth() - getPaddingLeft() - getPaddingRight();
            this.rectHeight = getHeight() - getPaddingTop() - getPaddingBottom();
        }
        postInvalidate();
    }

    public void setRect(float rectWidth, float rectHeight) {
        this.rectWidth = rectWidth;
        this.rectHeight = rectHeight;
        if (rectWidth > 0 && rectHeight > 0) {
            isRectSet = true;
        } else {
            isRectSet = false;
            this.rectWidth = getWidth() - getPaddingLeft() - getPaddingRight();
            this.rectHeight = getHeight() - getPaddingTop() - getPaddingBottom();
        }
        postInvalidate();
    }

    public void setRect(float rectWidth, float rectHeight, Matrix matrix) {
        this.rectWidth = rectWidth;
        this.rectHeight = rectHeight;
        if (rectWidth > 0 && rectHeight > 0) {
            isRectSet = true;
        } else {
            isRectSet = false;
            this.rectWidth = getWidth() - getPaddingLeft() - getPaddingRight();
            this.rectHeight = getHeight() - getPaddingTop() - getPaddingBottom();
        }
        setBaseMatrix(matrix);
    }

    public void setRect(float rectWidth, float rectHeight, Matrix matrix1, Matrix matrix2) {
        this.rectWidth = rectWidth;
        this.rectHeight = rectHeight;
        if (rectWidth > 0 && rectHeight > 0) {
            isRectSet = true;
        } else {
            isRectSet = false;
            this.rectWidth = getWidth() - getPaddingLeft() - getPaddingRight();
            this.rectHeight = getHeight() - getPaddingTop() - getPaddingBottom();
        }
        setMatrix(matrix1, matrix2);
    }

    public void setRect(float rectWidth, float rectHeight, Matrix matrix1, Matrix matrix2, Matrix matrix3) {
        this.rectWidth = rectWidth;
        this.rectHeight = rectHeight;
        if (rectWidth > 0 && rectHeight > 0) {
            isRectSet = true;
        } else {
            isRectSet = false;
            this.rectWidth = getWidth() - getPaddingLeft() - getPaddingRight();
            this.rectHeight = getHeight() - getPaddingTop() - getPaddingBottom();
        }
        setMatrix(matrix1, matrix2, matrix3);
    }

    public float getRectWidth() {
        return rectWidth;
    }

    public float getRectHeight() {
        return rectHeight;
    }

    public int getRectGravity() {
        return rectGravity;
    }

    public void setRectGravity(int rectGravity) {
        this.rectGravity = rectGravity;
        postInvalidate();
    }

    public int getRectAlpha() {
        return rectAlpha;
    }

    public void setRectAlpha(@IntRange(from = 0, to = 255) int rectAlpha) {
        this.rectAlpha = rectAlpha;
        rectPaint.setAlpha(rectAlpha);
        postInvalidate();
    }

    public int getOutOfRectAlpha() {
        return alpha;
    }

    public void setOutOfRectAlpha(@IntRange(from = 0, to = 255) int alpha) {
        this.alpha = alpha;
        bgPaint.setAlpha(alpha);
        postInvalidate();
    }

    public RectF getRect() {
        PointF start = getStartPoint(getWidth(), getHeight(), rectWidth, rectHeight);
        return new RectF(start.x, start.y, start.x + rectWidth, start.y + rectHeight);
    }

    public RectF getRect(float width, float height, float rectWidth, float rectHeight) {
        PointF start = getStartPoint(width, height, rectWidth, rectHeight);
        return new RectF(start.x, start.y, start.x + rectWidth, start.y + rectHeight);
    }

    /**
     * 获取中心区域的坐标
     *
     * @return
     */
    public PointF getRectCenter() {
        PointF start = getStartPoint();
//        rectCX = start.x + rectWidth / 2;
//        rectCY = start.y + rectHeight / 2;
        rectCX = start.x + getBorderLeft() + (rectWidth - getBorderLeft() - getBorderRight()) / 2;
        rectCY = start.y + getBorderTop() + (rectHeight - getBorderTop() - getBorderBottom()) / 2;

        return new PointF(rectCX, rectCY);
    }

    /**
     * 获取图片中心点坐标
     *
     * @return
     */
    public PointF getBitmapCenter() {
        float[] center = new float[2];
        bitmapMatrix.mapPoints(center, new float[]{bitmap.getWidth() * 1f / 2, bitmap.getHeight() * 1f / 2});
        return new PointF(center[0], center[1]);
    }

    /**
     * 获取图片经matrix变换后的中心坐标
     *
     * @param matrix
     * @return
     */
    public PointF getBitmapCenterWithMatrix(Matrix matrix) {
        if (matrix != null) {
            float[] center = new float[2];
            matrix.mapPoints(center, new float[]{bitmap.getWidth() * 1f / 2, bitmap.getHeight() * 1f / 2});
            return new PointF(center[0], center[1]);
        }
        return new PointF();
    }

    /**
     * 获取Bitmap当前点坐标
     * leftTop(0,1) rightTop(2,3)
     * leftBottom(4,5) rightBottom(6,7)
     * center(8,9)
     *
     * @return
     */
    public float[] getBitmapPoints() {
        float[] mapPoints = new float[10];
        mapPoints[0] = 0;
        mapPoints[1] = 0;

        mapPoints[2] = bitmap.getWidth();
        mapPoints[3] = 0;

        mapPoints[4] = 0;
        mapPoints[5] = bitmap.getHeight();

        mapPoints[6] = bitmap.getWidth();
        mapPoints[7] = bitmap.getHeight();

        mapPoints[8] = bitmap.getWidth() * 1f / 2;
        mapPoints[9] = bitmap.getHeight() * 1f / 2;


        float[] size = new float[mapPoints.length];
        Matrix drawMatrix = new Matrix();
        drawMatrix.postConcat(baseMatrix);
        drawMatrix.postConcat(canvasMatrix);
        drawMatrix.postConcat(bitmapMatrix);
        drawMatrix.mapPoints(size, mapPoints);
        return size;
    }

    public RectF getBitmapRectF(Matrix matrix) {
        RectF dst = new RectF();
        matrix.mapRect(dst, new RectF(0, 0, bitmap.getWidth(), bitmap.getHeight()));
        return dst;
    }

    public float[] getBitmapPoints(Matrix matrix) {
        float[] mapPoints = new float[10];
        mapPoints[0] = 0;
        mapPoints[1] = 0;

        mapPoints[2] = bitmap.getWidth();
        mapPoints[3] = 0;

        mapPoints[4] = 0;
        mapPoints[5] = bitmap.getHeight();

        mapPoints[6] = bitmap.getWidth();
        mapPoints[7] = bitmap.getHeight();

        mapPoints[8] = bitmap.getWidth() * 1f / 2;
        mapPoints[9] = bitmap.getHeight() * 1f / 2;


        float[] size = new float[mapPoints.length];
        matrix.mapPoints(size, mapPoints);
        return size;
    }

    /**
     * 移到中心
     */
    public void traranslateToCenter() {
        float[] bitmapPoints = getBitmapPoints();
        tempMatrix.set(bitmapMatrix);
        //移动到中心区域
        float dx = rectCX - bitmapPoints[8];
        float dy = rectCY - bitmapPoints[9];
        tempMatrix.postTranslate(dx, dy);
        setBitmapMatrix(tempMatrix);
    }

    /**
     * 缩放到内部区域大小
     *
     * @param isInside 是否全部居于内部区域中
     */
    public void scaleToContent(boolean isInside) {
        float[] bitmapPoints = getBitmapPoints();
        float width = bitmapPoints[6] - bitmapPoints[0];
        float height = bitmapPoints[7] - bitmapPoints[1];
        //缩放到指定大小
        float scaleX = rectWidth * 1f / width;
        float scaleY = rectHeight * 1f / height;
        float scale = 0;
        if (isInside) {
            scale = Math.min(scaleX, scaleY);
        } else {
            scale = Math.max(scaleX, scaleY);
        }
        tempMatrix.set(bitmapMatrix);
        tempMatrix.postScale(scale, scale, rectCX, rectCY);
        setBitmapMatrix(tempMatrix);
    }

    public Matrix postScale(float scaleX, float scaleY, float x, float y) {
        Matrix matrix = new Matrix();
        matrix.set(bitmapMatrix);
        matrix.postScale(scaleX, scaleY, x, y);
        setBitmapMatrix(matrix);
        return matrix;
    }

    public Matrix postScale(float scaleX, float scaleY) {
        Matrix matrix = new Matrix();
        matrix.set(bitmapMatrix);
        matrix.postScale(scaleX, scaleY, rectCX, rectCY);
        setBitmapMatrix(matrix);
        return matrix;
    }

    public Matrix postTranslate(float dx, float dy) {
        Matrix matrix = new Matrix();
        matrix.set(bitmapMatrix);
        matrix.postTranslate(dx, dy);
        setBitmapMatrix(matrix);
        return matrix;
    }

    // 点到直线的最短距离的判断 点（x0,y0） 到由两点组成的线段（x1,y1） ,( x2,y2 )
    private double pointToLine(PointF p0, PointF p1, PointF p) {
        return pointToLine(p0.x, p0.y, p1.x, p1.y, p.x, p.y);
    }

    private double pointToLine(double x1, double y1, double x2, double y2, double x0, double y0) {

        double space = 0;
        double a, b, c;
        a = lineSpace(x1, y1, x2, y2);// 线段的长度
        b = lineSpace(x1, y1, x0, y0);// (x1,y1)到点的距离
        c = lineSpace(x2, y2, x0, y0);// (x2,y2)到点的距离
        if (c <= 0.000001 || b <= 0.000001) {
            space = 0;
            return space;
        }
        if (a <= 0.000001) {
            space = b;
            return space;
        }
        if (c * c >= a * a + b * b) {
            space = b;
            return space;
        }
        if (b * b >= a * a + c * c) {
            space = c;
            return space;
        }
        double p = (a + b + c) / 2;// 半周长
        double s = Math.sqrt(p * (p - a) * (p - b) * (p - c));// 海伦公式求面积
        space = 2 * s / a;// 返回点到线的距离（利用三角形面积公式求高）
        return space;
    }

    // 计算两点之间的距离
    private double lineSpace(double x1, double y1, double x2, double y2) {
        double lineLength = 0;
        lineLength = Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2)
                * (y1 - y2));
        return lineLength;
    }

    //这是利用【向量积】判断点在【向量】的左侧还是右侧，向量是【有方向】的
    //v>0 点target在左侧
    //v=0target在线上
    //v<0 target在右侧
    private double isIn(PointF one, PointF two, PointF target) {
        return (two.x - one.x) * (target.y - one.y) - (target.x - one.x) * (two.y - one.y);
    }

    public boolean isInView(float x, float y) {
        return new RectF(0, 0, getWidth(), getHeight()).contains(x, y);//判断逆变换后的点击点是否在图像初始RectF内
    }

    public boolean isInRectView(float x, float y) {
        PointF start = getStartPoint();
        return new RectF(start.x / 3, start.y / 3, start.x * 5 / 3 + rectWidth, start.y * 5 / 3 + rectHeight).contains(x, y);//判断逆变换后的点击点是否在图像初始RectF内
    }

    public boolean isInRect(float x, float y, RectF rectF) {
        return rectF.contains(x, y);//判断逆变换后的点击点是否在图像初始RectF内
    }

    public boolean contains(float x, float y, Matrix matrix) {
        float[] invertPoint = new float[2];//逆变换后的点击点数组
        Matrix invertMatrix = new Matrix();//当前Matrix矩阵的逆矩阵
        matrix.invert(invertMatrix);//通过当前Matrix得到对应的逆矩阵数据
        invertMatrix.mapPoints(invertPoint, new float[]{x, y});//通过逆矩阵变化得到逆变换后的点击点
        RectF boundSrc = new RectF(0, 0, bitmap.getWidth(), bitmap.getHeight());

        return boundSrc.contains(invertPoint[0], invertPoint[1]);//判断逆变换后的点击点是否在图像初始RectF内
    }

    public boolean contains(float x, float y, @NonNull Matrix matrix, @NonNull RectF boundSrc) {
        float[] invertPoint = new float[2];//逆变换后的点击点数组
        Matrix invertMatrix = new Matrix();//当前Matrix矩阵的逆矩阵
        matrix.invert(invertMatrix);//通过当前Matrix得到对应的逆矩阵数据
        invertMatrix.mapPoints(invertPoint, new float[]{x, y});//通过逆矩阵变化得到逆变换后的点击点

        return boundSrc.contains(invertPoint[0], invertPoint[1]);//判断逆变换后的点击点是否在图像初始RectF内
    }

    private double getPoint2LineHeight(PointF point, PointF center, PointF p1, PointF p2, PointF p3, PointF p4, Matrix drawMatrix) {
        double d1 = pointToLine(p1, p2, point);
        double d2 = pointToLine(p1, p3, point);
        double d3 = pointToLine(p2, p4, point);
        double d4 = pointToLine(p3, p4, point);
        boolean contains = false;
        if (contains(point.x, point.y, drawMatrix)) {
            contains = true;
        }

//        System.out.println("getPoint2LineHeight: " + d1 + "  " + d2 + "  " + d3 + "  " + d4);
        if (d1 <= d2 && d1 <= d3 && d1 <= d4) {
            //d1最小
            double radius = pointToLine(p1, p2, center);
            if (contains) {
                d1 = Math.floor(d1);
                return (radius - d1) / radius;
            }
            d1 = Math.ceil(d1);
            return (radius + d1) / radius;
        } else if (d2 <= d1 && d2 <= d3 && d2 <= d4) {
            //d2最小
            double radius = pointToLine(p1, p3, center);
            if (contains) {
                d2 = Math.floor(d2);
                return (radius - d2) / radius;
            }
            d2 = Math.ceil(d2);
            return (radius + d2) / radius;
        } else if (d3 <= d2 && d3 <= d1 && d3 <= d4) {
            //d3最小
            double radius = pointToLine(p2, p4, center);
            if (contains) {
                d3 = Math.floor(d3);
                return (radius - d3) / radius;
            }
            d3 = Math.ceil(d3);
            return (radius + d3) / radius;
        } else if (d4 <= d2 && d4 <= d3 && d4 <= d1) {
            //d4最小
            double radius = pointToLine(p3, p4, center);
            if (contains) {
                d4 = Math.floor(d4);
                return (radius - d4) / radius;
            }
            d4 = Math.ceil(d4);
            return (radius + d4) / radius;
        }

        return 0;
    }

    private double getScaleSize(Matrix drawMatrix) {
        PointF center = getRectCenter();

        float[] points = getBitmapPoints(drawMatrix);
        PointF lt_ = new PointF(), rb_ = new PointF();
        boolean isLinear = true;
        if (points[0] == points[4] && points[2] == points[6] &&
                points[1] == points[3] && points[5] == points[7]) {
            //处于水平方向
            lt_.set(Math.min(points[0], points[2]), Math.min(points[1], points[5]));
            rb_.set(Math.max(points[0], points[2]), Math.max(points[1], points[5]));
        } else if (points[0] == points[2] && points[4] == points[6] &&
                points[1] == points[5] && points[3] == points[7]) {
            //处于竖直方向
            lt_.set(Math.min(points[0], points[4]), Math.min(points[1], points[3]));
            rb_.set(Math.max(points[0], points[4]), Math.max(points[1], points[3]));
        } else {
            isLinear = false;
        }

        if (isLinear) {
            float scaleX1 = rectWidth / (Math.abs(rb_.x - center.x) * 2);
            float scaleX2 = rectWidth / (Math.abs(center.x - lt_.x) * 2);
            float scaleY1 = rectHeight / (Math.abs(rb_.y - center.y) * 2);
            float scaleY2 = rectHeight / (Math.abs(center.y - lt_.y) * 2);
            float scale = Math.max(Math.max(scaleX1, scaleX2), Math.max(scaleY1, scaleY2));
            return scale;
        }

        PointF lt = getStartPoint();
        PointF rt = new PointF(lt.x + rectWidth, lt.y);
        PointF lb = new PointF(lt.x, lt.y + rectHeight);
        PointF rb = new PointF(lt.x + rectWidth, lt.y + rectHeight);

        PointF p1 = new PointF(points[0], points[1]);
        PointF p2 = new PointF(points[2], points[3]);
        PointF p3 = new PointF(points[4], points[5]);
        PointF p4 = new PointF(points[6], points[7]);

//        System.out.println(lt.toString() + "  " + rectWidth + "*" + rectHeight);
//        System.out.println(Arrays.toString(points));
        // 4条线:  p1p2  p1p3  p2p4  p3p4


        double d1 = 1;
        if (lt.equals(p1) || lt.equals(p2) || lt.equals(p3) || lt.equals(p4)) {
            //点重合
        } else {
            d1 = getPoint2LineHeight(lt, center, p1, p2, p3, p4, drawMatrix);
        }

        double d2 = 1;
        if (rt.equals(p1) || rt.equals(p2) || rt.equals(p3) || rt.equals(p4)) {
            //点重合
        } else {
            d2 = getPoint2LineHeight(rt, center, p1, p2, p3, p4, drawMatrix);
        }

        double d3 = 1;
        if (lb.equals(p1) || lb.equals(p2) || lb.equals(p3) || lb.equals(p4)) {
            //点重合
        } else {
            d3 = getPoint2LineHeight(lb, center, p1, p2, p3, p4, drawMatrix);
        }

        double d4 = 1;
        if (rb.equals(p1) || rb.equals(p2) || rb.equals(p3) || rb.equals(p4)) {
            //点重合
        } else {
            d4 = getPoint2LineHeight(rb, center, p1, p2, p3, p4, drawMatrix);
        }

//        System.out.println("getScaleSize: " + d1 + "  " + d2 + "  " + d3 + "  " + d4);
        d1 = Math.ceil(d1 * 100) / 100;
        d2 = Math.ceil(d2 * 100) / 100;
        d3 = Math.ceil(d3 * 100) / 100;
        d4 = Math.ceil(d4 * 100) / 100;
//        System.out.println("getScaleSize: " + d1 + "  " + d2 + "  " + d3 + "  " + d4);
        double d = 1;
        if (d1 < 1 && d2 < 1 && d3 < 1 && d4 < 1) {
            d1 = Math.min(d1, 1);
            d2 = Math.min(d2, 1);
            d3 = Math.min(d3, 1);
            d4 = Math.min(d4, 1);
        } else {
            d1 = Math.max(d1, 1);
            d2 = Math.max(d2, 1);
            d3 = Math.max(d3, 1);
            d4 = Math.max(d4, 1);
        }
        d = Math.max(d1, Math.max(d2, Math.max(d3, d4)));
//        System.out.println("d = " + d);
        return d;
    }

    //旋转
    public Matrix rotation(float degrees) {
        Matrix matrix = new Matrix();
        matrix.set(getBitmapMatrix());
        matrix.postRotate(degrees, rectCX, rectCY);
        if (autoSize && degrees != 0) {
            Matrix drawMatrix = new Matrix();
            drawMatrix.postConcat(baseMatrix);
            drawMatrix.postConcat(canvasMatrix);
            drawMatrix.postConcat(matrix);

            double d = getScaleSize(drawMatrix);

            if (d > 0)
                matrix.postScale((float) d, (float) d, rectCX, rectCY);
        }
        setBitmapMatrix(matrix);
        return matrix;
    }

    //旋转90
    public Matrix rotation() {
        Matrix matrix = new Matrix();
        matrix.set(getBitmapMatrix());
        matrix.postRotate(90, rectCX, rectCY);
        if (autoSize) {
            Matrix drawMatrix = new Matrix();
            drawMatrix.postConcat(baseMatrix);
            drawMatrix.postConcat(canvasMatrix);
            drawMatrix.postConcat(matrix);

            double d = getScaleSize(drawMatrix);

            if (d > 0)
                matrix.postScale((float) d, (float) d, rectCX, rectCY);
        }
        setBitmapMatrix(matrix);
        return matrix;
    }

    //旋转到坐标上
    public Matrix rotationTo() {
        Matrix matrix = new Matrix();
        matrix.set(getBitmapMatrix());
        float angle = getCurrentAngle();
        if (angle > -5 && angle <= 85) {
            matrix.postRotate(90 - angle, rectCX, rectCY);
        } else if (angle > 85 && angle <= 175) {
            matrix.postRotate(180 - angle, rectCX, rectCY);
        } else if ((angle >= -180 && angle <= -95) || angle > 175) {
            matrix.postRotate(-90 - angle, rectCX, rectCY);
        } else if (angle > -95 && angle <= 0) {
            matrix.postRotate(0 - angle, rectCX, rectCY);
        }
        if (autoSize) {
            Matrix drawMatrix = new Matrix();
            drawMatrix.postConcat(baseMatrix);
            drawMatrix.postConcat(canvasMatrix);
            drawMatrix.postConcat(matrix);

            double d = getScaleSize(drawMatrix);

            if (d > 0)
                matrix.postScale((float) d, (float) d, rectCX, rectCY);
        }
        setBitmapMatrix(matrix);
        return matrix;
    }

    /**
     * 根据内部区域位置获取开始点的坐标
     *
     * @return
     */
    public PointF getStartPoint() {
        float startX = getPaddingLeft();
        float startY = getPaddingTop();
        if (rectGravity == Gravity.RIGHT) {
            startX += (getWidth() - getPaddingLeft() - getPaddingRight() - rectWidth);
        } else if (rectGravity == Gravity.BOTTOM) {
            startY += (getHeight() - getPaddingTop() - getPaddingBottom() - rectHeight);
        } else if (rectGravity == (Gravity.RIGHT | Gravity.BOTTOM)) {
            startX += (getWidth() - getPaddingLeft() - getPaddingRight() - rectWidth);
            startY += (getHeight() - getPaddingTop() - getPaddingBottom() - rectHeight);
        } else if (rectGravity == Gravity.CENTER) {
            startX += ((getWidth() - getPaddingLeft() - getPaddingRight() - rectWidth) * 1f / 2);
            startY += ((getHeight() - getPaddingTop() - getPaddingBottom() - rectHeight) * 1f / 2);
        } else if (rectGravity == Gravity.CENTER_HORIZONTAL) {
            startX += ((getWidth() - getPaddingLeft() - getPaddingRight() - rectWidth) * 1f / 2);
        } else if (rectGravity == Gravity.CENTER_VERTICAL) {
            startY += ((getHeight() - getPaddingTop() - getPaddingBottom() - rectHeight) * 1f / 2);
        } else if (rectGravity == (Gravity.CENTER_VERTICAL | Gravity.RIGHT)) {
            startX += (getWidth() - getPaddingLeft() - getPaddingRight() - rectWidth);
            startY += ((getHeight() - getPaddingTop() - getPaddingBottom() - rectHeight) * 1f / 2);
        } else if (rectGravity == (Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM)) {
            startX += ((getWidth() - getPaddingLeft() - getPaddingRight() - rectWidth) * 1f / 2);
            startY += (getHeight() - getPaddingTop() - getPaddingBottom() - rectHeight);
        }
        return new PointF(startX, startY);
    }

    public PointF getStartPoint(float rectWidth, float rectHeight) {
        float startX = getPaddingLeft();
        float startY = getPaddingTop();
        if (rectGravity == Gravity.RIGHT) {
            startX += (getWidth() - getPaddingLeft() - getPaddingRight() - rectWidth);
        } else if (rectGravity == Gravity.BOTTOM) {
            startY += (getHeight() - getPaddingTop() - getPaddingBottom() - rectHeight);
        } else if (rectGravity == (Gravity.RIGHT | Gravity.BOTTOM)) {
            startX += (getWidth() - getPaddingLeft() - getPaddingRight() - rectWidth);
            startY += (getHeight() - getPaddingTop() - getPaddingBottom() - rectHeight);
        } else if (rectGravity == Gravity.CENTER) {
            startX += ((getWidth() - getPaddingLeft() - getPaddingRight() - rectWidth) * 1f / 2);
            startY += ((getHeight() - getPaddingTop() - getPaddingBottom() - rectHeight) * 1f / 2);
        } else if (rectGravity == Gravity.CENTER_HORIZONTAL) {
            startX += ((getWidth() - getPaddingLeft() - getPaddingRight() - rectWidth) * 1f / 2);
        } else if (rectGravity == Gravity.CENTER_VERTICAL) {
            startY += ((getHeight() - getPaddingTop() - getPaddingBottom() - rectHeight) * 1f / 2);
        } else if (rectGravity == (Gravity.CENTER_VERTICAL | Gravity.RIGHT)) {
            startX += (getWidth() - getPaddingLeft() - getPaddingRight() - rectWidth);
            startY += ((getHeight() - getPaddingTop() - getPaddingBottom() - rectHeight) * 1f / 2);
        } else if (rectGravity == (Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM)) {
            startX += ((getWidth() - getPaddingLeft() - getPaddingRight() - rectWidth) * 1f / 2);
            startY += (getHeight() - getPaddingTop() - getPaddingBottom() - rectHeight);
        }
        return new PointF(startX, startY);
    }

    public PointF getStartPoint(float width, float height, float rectWidth, float rectHeight) {
        float startX = getPaddingLeft();
        float startY = getPaddingTop();
        if (rectGravity == Gravity.RIGHT) {
            startX += (width - getPaddingLeft() - getPaddingRight() - rectWidth);
        } else if (rectGravity == Gravity.BOTTOM) {
            startY += (height - getPaddingTop() - getPaddingBottom() - rectHeight);
        } else if (rectGravity == (Gravity.RIGHT | Gravity.BOTTOM)) {
            startX += (width - getPaddingLeft() - getPaddingRight() - rectWidth);
            startY += (height - getPaddingTop() - getPaddingBottom() - rectHeight);
        } else if (rectGravity == Gravity.CENTER) {
            startX += ((width - getPaddingLeft() - getPaddingRight() - rectWidth) * 1f / 2);
            startY += ((height - getPaddingTop() - getPaddingBottom() - rectHeight) * 1f / 2);
        } else if (rectGravity == Gravity.CENTER_HORIZONTAL) {
            startX += ((width - getPaddingLeft() - getPaddingRight() - rectWidth) * 1f / 2);
        } else if (rectGravity == Gravity.CENTER_VERTICAL) {
            startY += ((height - getPaddingTop() - getPaddingBottom() - rectHeight) * 1f / 2);
        } else if (rectGravity == (Gravity.CENTER_VERTICAL | Gravity.RIGHT)) {
            startX += (width - getPaddingLeft() - getPaddingRight() - rectWidth);
            startY += ((height - getPaddingTop() - getPaddingBottom() - rectHeight) * 1f / 2);
        } else if (rectGravity == (Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM)) {
            startX += ((width - getPaddingLeft() - getPaddingRight() - rectWidth) * 1f / 2);
            startY += (height - getPaddingTop() - getPaddingBottom() - rectHeight);
        }
        return new PointF(startX, startY);
    }

    @Override
    protected void dispatchDraw(Canvas canvas) {
        super.dispatchDraw(canvas);

        if (bitmap == null || bitmap.isRecycled()) {
            return;
        }
        Matrix drawMatrix = new Matrix();
        drawMatrix.postConcat(baseMatrix);
        drawMatrix.postConcat(canvasMatrix);
        drawMatrix.postConcat(bitmapMatrix);
        //整个控件区域绘制，背景层
        int bgLayer = canvas.saveLayer(0, 0, getWidth(), getHeight(), new Paint(), Canvas.ALL_SAVE_FLAG);
        canvas.drawBitmap(bitmap, drawMatrix, bgPaint);
        canvas.restoreToCount(bgLayer);

        //内部区域位置
        PointF start = getStartPoint();
        RectF content = new RectF(start.x, start.y, rectWidth + start.x, rectHeight + start.y);
//        rectCX = start.x + rectWidth / 2;
//        rectCY = start.y + rectHeight / 2;
        rectCX = start.x + getBorderLeft() + (rectWidth - getBorderLeft() - getBorderRight()) / 2;
        rectCY = start.y + getBorderTop() + (rectHeight - getBorderTop() - getBorderBottom()) / 2;


        //内部区域，中心层
        int contentLayer = canvas.saveLayer(content, new Paint(), Canvas.ALL_SAVE_FLAG);
        canvas.drawBitmap(bitmap, drawMatrix, bitmapPaint);
        canvas.restoreToCount(contentLayer);


        //画覆盖在内部区域层上的border边框，边框层
        int borderLayer = canvas.saveLayer(content, new Paint(), Canvas.ALL_SAVE_FLAG);
        RectF border = new RectF(start.x + borderLeft, start.y + borderTop, start.x + rectWidth - borderRight, start.y + rectHeight - borderBottom);
        //content与border之间区域为边框区域
        canvas.drawRect(content, borderPaint);
        paint.setStyle(Paint.Style.FILL);
        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
        canvas.drawRect(border, paint);
        paint.setXfermode(null);
        canvas.restoreToCount(borderLayer);

        paint.setStyle(Paint.Style.STROKE);
        paint.setColor(Color.BLACK);
        canvas.drawRect(content, paint);


        if (isPointTouch && showTouchLine) {
            paint.setStyle(Paint.Style.STROKE);
            paint.setColor(lineColor);
            Path path = new Path();
            path.moveTo(start.x, start.y + rectHeight / 3);
            path.lineTo(start.x + rectWidth, start.y + rectHeight / 3);
            path.moveTo(start.x, start.y + rectHeight * 2 / 3);
            path.lineTo(start.x + rectWidth, start.y + rectHeight * 2 / 3);
            path.moveTo(start.x + rectWidth / 3, start.y);
            path.lineTo(start.x + rectWidth / 3, start.y + rectHeight);
            path.moveTo(start.x + rectWidth * 2 / 3, start.y);
            path.lineTo(start.x + rectWidth * 2 / 3, start.y + rectHeight);
            canvas.drawPath(path, paint);
        }

        if (rectDrawListener != null) {
            rectDrawListener.onDraw(canvas, start.x, start.y, rectWidth, rectHeight);
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (touchListener != null) {
            //消费onTouchEvent事件
            if (touchListener.onTouch(this, event)) {
                return true;
            }
        }

        //执行伸缩、拖拽、点击事件
        int action = MotionEventCompat.getActionMasked(event);
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                restoreMatrix.set(bitmapMatrix);
                //第一个手指按下
                PointF start = getStartPoint();
                if (event.getX() < start.x + borderLeft || event.getX() > start.x + rectWidth - borderRight || event.getY() < start.y + borderTop || event.getY() > start.y + rectHeight - borderBottom) {
                    //点击的点在内部区域外
                    System.out.println("view touch out of rect.");
                } else {
                    isPointTouch = true;
                    mActionMode = ActionMode.CLICK;
                    downPoint.set(event.getX(), event.getY());
                    tempMatrix.set(bitmapMatrix);
                    saveMatrix.set(tempMatrix);
                    clickTime = System.currentTimeMillis();
                    if (lastClickTime == 0)
                        lastClickTime = clickTime;
                    invalidate();
                }
                break;

            case MotionEvent.ACTION_POINTER_DOWN:
                if (mActionMode == ActionMode.CLICK && zoom) {
                    //第二个手指按下
                    downPoints[0] = new PointF(event.getX(0), event.getY(0));
                    downPoints[1] = new PointF(event.getX(1), event.getY(1));
                    downPointDistance = calculateDistance(event);
                    mMidPoint = calculateMidPoint(event);
                    mActionMode = ActionMode.ZOOM;
                }
                break;

            case MotionEvent.ACTION_MOVE:
                //滑动
                if (mActionMode == ActionMode.DRAG && drag) {
                    //拖动
                    if (dragListener != null) {
                        //拖动中
                        if (dragListener.onDraging(saveMatrix, downPoint, new PointF(event.getX(), event.getY()))) {
                            //外部拦截消费
                            return true;
                        }
                    }
                    //外部不拦截内部拖动
                    tempMatrix.set(saveMatrix);
                    tempMatrix.postTranslate(event.getX() - downPoint.x, event.getY() - downPoint.y);

                    Matrix drawMatrix = new Matrix();
                    drawMatrix.postConcat(baseMatrix);
                    drawMatrix.postConcat(canvasMatrix);
                    drawMatrix.postConcat(tempMatrix);
                    PointF lt = getStartPoint();
                    PointF rt = new PointF(lt.x + rectWidth, lt.y);
                    PointF lb = new PointF(lt.x, lt.y + rectHeight);
                    PointF rb = new PointF(lt.x + rectWidth, lt.y + rectHeight);
                    boolean ltIn = contains(lt.x, lt.y, drawMatrix);
                    boolean rtIn = contains(rt.x, rt.y, drawMatrix);
                    boolean lbIn = contains(lb.x, lb.y, drawMatrix);
                    boolean rbIn = contains(rb.x, rb.y, drawMatrix);
                    if (ltIn && rtIn && lbIn && rbIn) {
                        //用于恢复
                        restoreMatrix.set(tempMatrix);
                    } else {
                        float[] points = getBitmapPoints(drawMatrix);
                        if (!isInRectView(points[0], points[1])
                                && !isInRectView(points[2], points[3])
                                && !isInRectView(points[4], points[5])
                                && !isInRectView(points[6], points[7])
                                && !isInRectView(points[8], points[9])
                                && !ltIn && !rtIn && !lbIn && !rbIn) {
                            //图像在区域外，不再缩放
                            return true;
                        }
                    }
                    setBitmapMatrix(tempMatrix);
                } else if (mActionMode == ActionMode.ZOOM && zoom) {
                    //缩放
                    if (zoomListener != null) {
                        if (zoomListener.onZooming(saveMatrix, downPoints[0], downPoints[1], new PointF(event.getX(0), event.getY(0)), new PointF(event.getX(1), event.getY(1))))
                            return true;
                    }
                    float distance = calculateDistance(event);
                    float scale = distance / downPointDistance;
                    tempMatrix.set(saveMatrix);
                    tempMatrix.postScale(scale, scale, mMidPoint.x, mMidPoint.y);

                    Matrix drawMatrix = new Matrix();
                    drawMatrix.postConcat(baseMatrix);
                    drawMatrix.postConcat(canvasMatrix);
                    drawMatrix.postConcat(tempMatrix);
                    PointF lt = getStartPoint();
                    PointF rt = new PointF(lt.x + rectWidth, lt.y);
                    PointF lb = new PointF(lt.x, lt.y + rectHeight);
                    PointF rb = new PointF(lt.x + rectWidth, lt.y + rectHeight);
                    float[] points = getBitmapPoints(drawMatrix);
                    float nw = calculateDistance(points[0], points[1], points[2], points[3]);
                    float nh = calculateDistance(points[0], points[1], points[4], points[5]);
                    if (nw * nh < rectWidth * rectHeight * 0.01) {
                        //图像太小，不再缩放
                    } else {
                        boolean ltIn = contains(lt.x, lt.y, drawMatrix);
                        boolean rtIn = contains(rt.x, rt.y, drawMatrix);
                        boolean lbIn = contains(lb.x, lb.y, drawMatrix);
                        boolean rbIn = contains(rb.x, rb.y, drawMatrix);
                        if (ltIn && rtIn && lbIn && rbIn) {
                            //用于恢复
                            restoreMatrix.set(tempMatrix);
                        } else {
                            if (!isInRectView(points[0], points[1])
                                    && !isInRectView(points[2], points[3])
                                    && !isInRectView(points[4], points[5])
                                    && !isInRectView(points[6], points[7])
                                    && !isInRectView(points[8], points[9])
                                    && !ltIn && !rtIn && !lbIn && !rbIn) {
                                //图像在区域外，不再缩放
                                return true;
                            }
                        }

                        setBitmapMatrix(tempMatrix);
                    }
                } else if (mActionMode == ActionMode.CLICK) {
                    //稍微滑动记为点击
                    if (Math.abs(event.getX() - downPoint.x) > 10 || Math.abs(event.getY() - downPoint.y) > 10) {
                        downPoint.set(event.getX(), event.getY());
                        mActionMode = ActionMode.DRAG;
                    }
                }
                break;

            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                isPointTouch = false;
                if (mActionMode == ActionMode.DRAG && drag) {
                    //拖动
                    if (autoSize) {
                        //拖动结束，判断边缘是否过度移动
                        tempMatrix.set(restoreMatrix);
                        PointF startPoint = getStartPoint();
                        float[] points = getBitmapPoints();
                        float dx = 0, dy = 0;
                        PointF lt = new PointF(), rb = new PointF();
                        boolean isLinear = true;
                        if (points[0] == points[4] && points[2] == points[6] &&
                                points[1] == points[3] && points[5] == points[7]) {
                            //处于水平方向
                            lt.set(Math.min(points[0], points[2]), Math.min(points[1], points[5]));
                            rb.set(Math.max(points[0], points[2]), Math.max(points[1], points[5]));
                        } else if (points[0] == points[2] && points[4] == points[6] &&
                                points[1] == points[5] && points[3] == points[7]) {
                            //处于水平方向
                            lt.set(Math.min(points[0], points[4]), Math.min(points[1], points[3]));
                            rb.set(Math.max(points[0], points[4]), Math.max(points[1], points[3]));
                        } else {
                            isLinear = false;
                        }

                        if (isLinear) {
                            if ((startPoint.x >= lt.x)
                                    && (startPoint.x + rectWidth <= rb.x)) {
                                //
                            } else {
                                if (startPoint.x < lt.x) {
                                    dx = startPoint.x - lt.x;
                                } else if (startPoint.x + rectWidth > rb.x) {
                                    dx = startPoint.x + rectWidth - rb.x;
                                } else {
                                    dx = startPoint.x + rectWidth / 2 - (lt.x + (rb.x - lt.x) / 2);
                                }
                            }

                            if ((startPoint.y >= lt.y) && (startPoint.y + rectHeight <= rb.y)) {
                                //
                            } else {
                                if (startPoint.y < lt.y) {
                                    dy = startPoint.y - lt.y;
                                } else if (startPoint.y + rectHeight > rb.y) {
                                    dy = startPoint.y + rectHeight - rb.y;
                                } else {
                                    dy = startPoint.y + rectHeight / 2 - (lt.y + (rb.y - lt.y) / 2);
                                }
                            }

                            tempMatrix.set(getBitmapMatrix());
                            tempMatrix.postTranslate(dx, dy);
                        }
                        setBitmapMatrix(tempMatrix);
                    }
                    if (dragListener != null) {
                        dragListener.onDrag(saveMatrix, downPoint, new PointF(event.getX(), event.getY()));
                    }
                } else if (mActionMode == ActionMode.CLICK) {
                    //点击
                    if (clickTime - lastClickTime > 0 && clickTime - lastClickTime < 500) {
                        //2秒内点击多次为doubleClick事件
                        handler.removeCallbacksAndMessages(null);
                        if (doubleClickListener != null)
                            doubleClickListener.onDoubleClick(this);
                    } else if (System.currentTimeMillis() - clickTime > 1000) {
                        //单次长按3秒以上为longClick事件
                        performLongClick();
                    } else {
                        if (doubleClickListener == null) {
                            performClick();
                        } else {
                            handler.postDelayed(new Runnable() {
                                @Override
                                public void run() {
                                    performClick();
                                }
                            }, 500);
                        }
                    }
                    lastClickTime = clickTime;
                    clickTime = 0;
                }
                mActionMode = ActionMode.NONE;
                invalidate();
                break;

            case MotionEvent.ACTION_POINTER_UP:
                if (mActionMode == ActionMode.ZOOM && zoom) {
                    //缩放
                    if (autoSize) {
                        tempMatrix.set(restoreMatrix);
                        PointF startPoint = getStartPoint();
                        float[] points = getBitmapPoints();
                        PointF lt = new PointF(), rb = new PointF();
                        boolean isLinear = true;
                        if (points[0] == points[4] && points[2] == points[6] &&
                                points[1] == points[3] && points[5] == points[7]) {
                            //处于水平方向
                            lt.set(Math.min(points[0], points[2]), Math.min(points[1], points[5]));
                            rb.set(Math.max(points[0], points[2]), Math.max(points[1], points[5]));
                        } else if (points[0] == points[2] && points[4] == points[6] &&
                                points[1] == points[5] && points[3] == points[7]) {
                            //处于水平方向
                            lt.set(Math.min(points[0], points[4]), Math.min(points[1], points[3]));
                            rb.set(Math.max(points[0], points[4]), Math.max(points[1], points[3]));
                        } else {
                            isLinear = false;
                        }

                        if (isLinear) {
                            if ((startPoint.x < lt.x) && (startPoint.x + rectWidth > rb.x)
                                    && (startPoint.y < lt.y) && (startPoint.y + rectHeight > rb.y)) {
                                //图片全部在内部区域内，居中充满
                                float dx = startPoint.x + rectWidth / 2 - (lt.x + (rb.x - lt.x) / 2);
                                float dy = startPoint.y + rectHeight / 2 - (lt.y + (rb.y - lt.y) / 2);
                                float scaleX = rectWidth / (rb.x - lt.x);
                                float scaleY = rectHeight / (rb.y - lt.y);
                                float scale = Math.max(scaleX, scaleY);
                                tempMatrix.set(getBitmapMatrix());
                                tempMatrix.postTranslate(dx, dy);
                                tempMatrix.postScale(scale, scale, startPoint.x + rectWidth / 2, startPoint.y + rectHeight / 2);
                            }
                        }
                        setBitmapMatrix(tempMatrix);
                    }
                    if (zoomListener != null) {
                        zoomListener.onZoom(saveMatrix, downPoints[0], downPoints[1], new PointF(event.getX(0), event.getY(0)), new PointF(event.getX(1), event.getY(1)));
                    }
                    mActionMode = ActionMode.NONE;
                }
                break;

        }

        return true;
    }

    public float getCurrentAngle() {
        return getMatrixAngle(bitmapMatrix);
    }

    public float getMatrixAngle(@NonNull Matrix matrix) {
        return (float) Math.toDegrees(-(Math.atan2(getMatrixValue(matrix, Matrix.MSKEW_X),
                getMatrixValue(matrix, Matrix.MSCALE_X))));
    }

    public float getMatrixValue(@NonNull Matrix matrix, @IntRange(from = 0, to = 9) int valueIndex) {
        matrix.getValues(matrixValues);
        return matrixValues[valueIndex];
    }

    protected PointF calculateMidPoint(@Nullable MotionEvent event) {
        PointF point = new PointF();
        if (event == null || event.getPointerCount() < 2) {
            point.set(0, 0);
            return point;
        }
        float x = (event.getX(0) + event.getX(1)) / 2;
        float y = (event.getY(0) + event.getY(1)) / 2;
        point.set(x, y);
        return point;
    }

    protected float calculateDistance(@Nullable MotionEvent event) {
        if (event == null || event.getPointerCount() < 2) {
            return 0f;
        }
        return calculateDistance(event.getX(0), event.getY(0), event.getX(1), event.getY(1));
    }

    protected float calculateDistance(float x1, float y1, float x2, float y2) {
        double x = x1 - x2;
        double y = y1 - y2;

        return (float) Math.sqrt(x * x + y * y);
    }

    public interface ActionMode {
        int NONE = -1;
        int CLICK = 0;
        int DRAG = 1;
        int ZOOM = 2;
    }

    private OnDragListener dragListener;
    private OnZoomListener zoomListener;
    private OnTouchListener touchListener;
    private OnDoubleClickListener doubleClickListener;
    private OnViewSizeChangedListener viewSizeChangedListener;
    private OnRectDrawListener rectDrawListener;

    public void setOnRectDrawListener(OnRectDrawListener rectDrawListener) {
        this.rectDrawListener = rectDrawListener;
    }

    public void setOnViewSizeChangedListener(OnViewSizeChangedListener viewSizeChangedListener) {
        this.viewSizeChangedListener = viewSizeChangedListener;
    }

    public void setOnDoubleClickListener(OnDoubleClickListener doubleClickListener) {
        this.doubleClickListener = doubleClickListener;
    }

    public void setOnTouchListener(OnTouchListener touchListener) {
        this.touchListener = touchListener;
    }

    public void setOnZoomListener(OnZoomListener zoomListener) {
        this.zoomListener = zoomListener;
    }

    public void setOnDragListener(OnDragListener dragListener) {
        this.dragListener = dragListener;
    }

    public interface OnDragListener {
        /**
         * 拖动中
         *
         * @param matrix     拖动前的矩阵
         * @param startPoint 按下的位置
         * @param movePoint  当前滑到的位置
         * @return
         */
        boolean onDraging(Matrix matrix, PointF startPoint, PointF movePoint);

        /**
         * 拖动结束
         *
         * @param matrix     拖动前的矩阵
         * @param startPoint 按下的位置
         * @param endPoint   滑动结束的位置
         */
        void onDrag(Matrix matrix, PointF startPoint, PointF endPoint);
    }

    public interface OnZoomListener {
        boolean onZooming(Matrix matrix, PointF downPoint1, PointF downPoint2, PointF movePoint1, PointF movePoint2);

        void onZoom(Matrix matrix, PointF downPoint1, PointF downPoint2, PointF endPoint1, PointF endPoint2);
    }

    public interface OnDoubleClickListener {
        void onDoubleClick(View view);
    }

    public interface OnViewSizeChangedListener {
        boolean onSizeChanged(int preWidth, int preHeight, int width, int height, float preRectWidth, float preRectHeight, float rectWidth, float rectHeight);
    }

    public interface OnRectDrawListener {
        void onDraw(Canvas canvas, float x, float y, float width, float height);
    }

    public static class Gravity {
        public static final int LEFT = 100;
        public static final int RIGHT = 101;
        public static final int TOP = 102;
        public static final int BOTTOM = 103;
        public static final int CENTER_HORIZONTAL = 201;
        public static final int CENTER_VERTICAL = 205;
        public static final int CENTER = 406;
    }

    public static final class LayerMatrix {
        String name;
        Matrix matrix;

        public LayerMatrix(String name, Matrix matrix) {
            this.name = name;
            this.matrix = matrix;
        }

        public String getName() {
            return name;
        }

        public Matrix getMatrix() {
            return matrix;
        }

        public boolean equals(LayerMatrix o) {
            if (o != null && this.name != null && this.name.equals(o.name))
                return true;

            return false;
        }

        public boolean equals(String o) {
            if (o != null && this.name != null && this.name.equals(o))
                return true;

            return false;
        }

        public boolean nameStartWith(String str) {
            if (str != null && this.name != null && this.name.startsWith(str)) {
                return true;
            }
            return false;
        }

        @Override
        public String toString() {
            return "name: " + name;
        }
    }
}
